home *** CD-ROM | disk | FTP | other *** search
- /*
- * tkGrab.c --
- *
- * This file provides procedures that implement grabs for Tk.
- *
- * Copyright 1992 Regents of the University of California.
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkGrab.c,v 1.18 92/08/07 09:55:31 ouster Exp $ SPRITE (Berkeley)";
- #endif
-
- #include "tkConfig.h"
- #include "tkInt.h"
-
- /*
- *-------------------------------------------------------------------
- * Problems with current grab implementation (8/7/92):
- *
- * 1. In a local grab the synthesized events are always placed at the
- * front of the event queue. If there are several grabs and ungrabs
- * in a row, the groups of events for the different grabs/ungrabs
- * end up in backwards order.
- * 2. The variables serverWinPtr and pointerWinPtr are hardly used at
- * all and should probably be eliminated.
- * 3. The fact that grabWinPtr is set at the time a grab is set or
- * released, rather than when its events are processed, means that
- * it can get out of sync with the event queue if there's a rapid
- * sequence of grabs or ungrabs. The only solution I can think of
- * is to keep a parallel queue to the event queue to update grabWinPtr
- * (or, synthesize an event to change the pointer?).
- *-------------------------------------------------------------------
- */
-
- /*
- * Bit definitions for grabFlags field of TkDisplay structures:
- *
- * GRAB_GLOBAL 1 means this is a global grab (we grabbed via
- * the server so all applications are locked out.
- * 0 means this is a local grab that affects
- * only this application.
- * GRAB_BUTTON_RELEASE 1 means that a button-release event just
- * occurred and we're in the middle of a sequence
- * of Enter and Leave events with NotifyUngrab
- * mode.
- */
-
- #define GRAB_GLOBAL 1
- #define GRAB_BUTTON_RELEASE 2
-
- /*
- * Forward declarations for procedures declared later in this file:
- */
-
- static void ChangeEventWindow _ANSI_ARGS_((XEvent *eventPtr,
- TkWindow *winPtr));
- static void MovePointer _ANSI_ARGS_((XEvent *eventPtr,
- TkWindow *sourcePtr, TkWindow *destPtr));
- static void MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
- TkWindow *destPtr, int mode));
-
- /*
- *----------------------------------------------------------------------
- *
- * Tk_GrabCmd --
- *
- * This procedure is invoked to process the "grab" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tk_GrabCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Main window associated with
- * interpreter. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- TkWindow *winPtr = (TkWindow *) clientData;
- int length, lockScreen;
- char *window;
-
- if (argc > 3) {
- badArgs:
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " ?-global? ?window?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (argc == 1) {
- if ((winPtr->dispPtr->grabWinPtr != NULL)
- && (winPtr->dispPtr->grabWinPtr->mainPtr
- == winPtr->mainPtr)) {
- interp->result = Tk_PathName(winPtr->dispPtr->grabWinPtr);
- } else {
- interp->result = "none";
- }
- return TCL_OK;
- }
- if (argc == 3) {
- length = strlen(argv[1]);
- if ((strncmp(argv[1], "-global", length) != 0) || (length < 2)) {
- goto badArgs;
- }
- lockScreen = 1;
- window = argv[2];
- } else {
- lockScreen = 0;
- window = argv[1];
- }
- if ((window[0] == '\0')
- || (strncmp(window, "none", strlen(window)) == 0)) {
- Tk_Ungrab((Tk_Window) winPtr);
- } else {
- Tk_Window tkwin;
-
- tkwin = Tk_NameToWindow(interp, window, (Tk_Window) winPtr);
- if (tkwin == NULL) {
- return TCL_ERROR;
- }
- return Tk_Grab(interp, tkwin, lockScreen);
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tk_Grab --
- *
- * Grabs the pointer and keyboard, so that mouse-related events are
- * only reported relative to a given window and its descendants.
- *
- * Results:
- * A standard Tcl result is returned. TCL_OK is the normal return
- * value; if the grab could not be set then TCL_ERROR is returned
- * and interp->result will hold an error message.
- *
- * Side effects:
- * Once this call completes successfully, no window outside the
- * tree rooted at tkwin will receive pointer- or keyboard-related
- * events until the next call to Tk_Ungrab. If a previous grab was
- * in effect within this application, then it is replaced with a new
- * one.
- *
- *----------------------------------------------------------------------
- */
-
- int
- Tk_Grab(interp, tkwin, grabGlobal)
- Tcl_Interp *interp; /* Used for error reporting. */
- Tk_Window tkwin; /* Window on whose behalf the pointer
- * is to be grabbed. */
- int grabGlobal; /* Non-zero means issue a grab to the
- * server so that no other application
- * gets mouse or keyboard events.
- * Zero means the grab only applies
- * within this application. */
- {
- int grabResult;
- TkWindow *winPtr = (TkWindow *) tkwin;
- TkDisplay *dispPtr = winPtr->dispPtr;
- int grabRequest, inSequence, ignoring, numEvents, i, diff;
- XEvent *events, *eventPtr;
- TkWindow *winPtr2;
-
- if (dispPtr->grabWinPtr != NULL) {
- if ((dispPtr->grabWinPtr == winPtr)
- && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
- return TCL_OK;
- }
- if (dispPtr->grabWinPtr->mainPtr != winPtr->mainPtr) {
- alreadyGrabbed:
- interp->result = "grab failed: another application has grab";
- return TCL_ERROR;
- }
- Tk_Ungrab(tkwin);
- }
-
- if (grabGlobal) {
- grabRequest = NextRequest(dispPtr->display);
- grabResult = XGrabPointer(dispPtr->display, Tk_WindowId(tkwin),
- True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
- GrabModeAsync, GrabModeAsync, None, None,
- TkCurrentTime(dispPtr));
- if (grabResult != 0) {
- grabError:
- if (grabResult == GrabNotViewable) {
- interp->result = "grab failed: window not viewable";
- } else if (grabResult == AlreadyGrabbed) {
- goto alreadyGrabbed;
- } else if (grabResult == GrabFrozen) {
- interp->result = "grab failed: keyboard or pointer frozen";
- } else if (grabResult == GrabInvalidTime) {
- interp->result = "grab failed: invalid time";
- } else {
- char msg[100];
-
- sprintf(msg, "grab failed for unknown reason (code %d)",
- grabResult);
- Tcl_AppendResult(interp, msg, (char *) NULL);
- }
- return TCL_ERROR;
- }
- grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
- False, GrabModeAsync, GrabModeAsync, TkCurrentTime(dispPtr));
- if (grabResult != 0) {
- XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
- goto grabError;
- }
- dispPtr->grabFlags |= GRAB_GLOBAL;
- } else {
- /*
- * The call to XUngrabPointer below is needed to release any
- * existing auto-grab due to a button press. This is needed
- * so that local grabs behave the same as global grabs (the
- * button grab is released by the X server in a global grab).
- */
-
- XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
- grabRequest = LastKnownRequestProcessed(dispPtr->display);
- dispPtr->grabFlags &= ~GRAB_GLOBAL;
-
- /*
- * Since we're not telling the server about the grab, we have
- * to generate Leave and Enter events to move the pointer from
- * its current window to the grab window.
- */
-
- MovePointer2(dispPtr->pointerWinPtr, winPtr, NotifyGrab);
- }
- dispPtr->grabWinPtr = winPtr;
-
- /*
- * When a grab occurs, X generates Enter and Leave events to move
- * the pointer from its current window to the grab window, even if
- * the current window is in the grab tree. We don't want these
- * events getting through to the application if the current window
- * is in the grab tree. In order to eliminate the bogus events,
- * process all pending events and filter out the bogus ones.
- *
- * Also, filter out the final enter event into the grab window in
- * any case: this event shouldn't be delivered until the mouse really
- * moves into that window.
- *
- * The code below reads in all the pending events, filters out the bad
- * ones, and then pushes back all the events that weren't filtered.
- * Another alternative would be to simply process the events
- * immediately rather than pushing them back again. However, this
- * tends to interfere with scripts since it causes pending events
- * to be processed during the "grab" command. The "grab" command
- * might have been invoked in the middle of some computation where
- * it's a bad idea to process new events.
- */
-
- XSync(dispPtr->display, False);
- numEvents = QLength(dispPtr->display);
- if (numEvents == 0) {
- return TCL_OK;
- }
- events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
- for (i = 0; i < numEvents; i++) {
- XNextEvent(dispPtr->display, &events[i]);
- }
- inSequence = ignoring = 0;
- for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
- if (((eventPtr->type != EnterNotify)
- && (eventPtr->type != LeaveNotify))
- || (eventPtr->xcrossing.mode != NotifyGrab)) {
- continue;
- }
-
- /*
- * The diff caculcation below is trickier than you might think,
- * due to the fact that the event serial number is unsigned and
- * serial numbers can wrap around.
- */
-
- diff = eventPtr->xcrossing.serial;
- diff -= grabRequest;
- if (!inSequence && (diff >= 0)) {
- /*
- * This is the first event of the grab sequence. See if its
- * window is in the grab tree and ignore the sequence if it is.
- */
-
- inSequence = 1;
- if (XFindContext(dispPtr->display, eventPtr->xcrossing.window,
- tkWindowContext, (caddr_t *) &winPtr2) == 0) {
- for ( ; winPtr2 != NULL; winPtr2 = winPtr2->parentPtr) {
- if (winPtr2 == dispPtr->grabWinPtr) {
- ignoring = 1;
- break;
- }
- }
- }
- }
- if (ignoring) {
- eventPtr->type = 0;
- }
- if (inSequence && (eventPtr->type == EnterNotify)
- && (dispPtr->grabWinPtr->window
- == eventPtr->xcrossing.window)) {
- eventPtr->type = 0;
- break;
- }
- }
- for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
- if (eventPtr->type != 0) {
- XPutBackEvent(dispPtr->display, eventPtr);
- }
- }
- ckfree((char *) events);
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tk_Ungrab --
- *
- * Releases a grab on the mouse pointer and keyboard.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Pointer and keyboard events will start being delivered to other
- * windows again.
- *
- *----------------------------------------------------------------------
- */
-
- void
- Tk_Ungrab(tkwin)
- Tk_Window tkwin; /* Window that identifies display
- * for grab to be released. */
- {
- TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
- int inSequence, ignoring, ungrabRequest, numEvents, i, j, diff;
- TkWindow *grabWinPtr, *winPtr;
- XEvent *events, *eventPtr, *eventPtr2;
-
- grabWinPtr = dispPtr->grabWinPtr;
- if (grabWinPtr == NULL) {
- return;
- }
- dispPtr->grabWinPtr = NULL;
- dispPtr->buttonWinPtr = NULL;
- if (dispPtr->grabFlags & GRAB_GLOBAL) {
- ungrabRequest = NextRequest(dispPtr->display);
- XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
- XUngrabKeyboard(dispPtr->display, TkCurrentTime(dispPtr));
- XSync(dispPtr->display, False);
- } else {
- ungrabRequest = LastKnownRequestProcessed(dispPtr->display);
- if ((dispPtr->ungrabWinPtr != NULL)
- && (dispPtr->ungrabWinPtr->mainPtr != grabWinPtr->mainPtr)) {
-
- /*
- * Don't report entries down into a window of a different
- * application, since it's already seen those entries earlier.
- */
-
- dispPtr->ungrabWinPtr = NULL;
- }
- MovePointer2(grabWinPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
- }
-
- /*
- * We have to filter all the pending events in a fashion similar to
- * Tk_Grab. As with grabs, the X server generates an Enter-Leave event
- * sequence to move the pointer from the grab window back to its
- * current window. We need to ignore this sequence if the pointer
- * is being moved to a window that's already in the grab tree.
- */
-
- numEvents = QLength(dispPtr->display);
- if (numEvents == 0) {
- return;
- }
- events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
- for (i = 0; i < numEvents; i++) {
- XNextEvent(dispPtr->display, &events[i]);
- }
- inSequence = ignoring = 0;
- for (i = numEvents-1, eventPtr = events; i >= 0; i--, eventPtr++) {
- if (((eventPtr->type != EnterNotify)
- && (eventPtr->type != LeaveNotify))
- || (eventPtr->xcrossing.mode != NotifyUngrab)) {
- continue;
- }
- diff = eventPtr->xcrossing.serial;
- diff -= ungrabRequest;
- if (!inSequence && (diff >= 0)) {
-
- /*
- * This is the first event of the ungrab sequence. Scan forward
- * looking for the final Enter event in the sequence. Then see
- * if that event's window is in the grab tree.
- */
-
- inSequence = 1;
- for (j = i, eventPtr2 = eventPtr; j >= 0; j--, eventPtr2++) {
- if (eventPtr2->type == EnterNotify) {
- if (eventPtr2->xcrossing.mode != NotifyUngrab) {
- break;
- }
- if ((eventPtr2->xcrossing.detail != NotifyAncestor)
- && (eventPtr2->xcrossing.detail != NotifyInferior)
- && (eventPtr2->xcrossing.detail
- != NotifyNonlinear)) {
- continue;
- }
- if (XFindContext(dispPtr->display,
- eventPtr2->xcrossing.window,
- tkWindowContext, (caddr_t *) &winPtr) == 0) {
- for ( ; winPtr != NULL; winPtr = winPtr->parentPtr) {
- if (winPtr == grabWinPtr) {
- ignoring = 1;
- break;
- }
- }
- }
- break;
- } else if ((eventPtr2->type != LeaveNotify)
- || (eventPtr2->xcrossing.mode != NotifyUngrab)) {
- break;
- }
- }
- }
- if (ignoring) {
- eventPtr->type = 0;
- }
- }
- for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
- if (eventPtr->type != 0) {
- XPutBackEvent(dispPtr->display, eventPtr);
- }
- }
- ckfree((char *) events);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkPointerEvent --
- *
- * This procedure is called for each pointer-related event, before
- * the event has been processed. It does various things to make
- * grabs work correctly.
- *
- * Results:
- * If the return value is 1 it means the event should be processed
- * (event handlers should be invoked). If the return value is 0
- * it means the event should be ignored in order to make grabs
- * work correctly. Note: the event may be modified by this procedure.
- *
- * Side effects:
- * Grab state information may be updated.
- *
- *----------------------------------------------------------------------
- */
-
- int
- TkPointerEvent(eventPtr, winPtr)
- register XEvent *eventPtr; /* Pointer to the event. */
- TkWindow *winPtr; /* Tk's information for window
- * where event was reported. */
- {
- register TkWindow *winPtr2;
- TkDisplay *dispPtr = winPtr->dispPtr;
- int outsideGrabTree = 0;
- int originalFlags;
- int appGrabbed = 0; /* Non-zero means event is being
- * reported to an application that is
- * affected by the grab. */
- #define ALL_BUTTONS \
- (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
- static unsigned int state[] = {
- Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
- };
-
- /*
- * Don't do any filtering on events generated by the event-sharing code.
- */
-
- if (eventPtr == tkShareEventPtr) {
- return 1;
- }
-
- /*
- * If a grab is in effect, see if the event is being reported to
- * a window in the grab tree. Also see if the event is being reported
- * to an application that is affected by the grab.
- */
-
- if (dispPtr->grabWinPtr != NULL) {
- if ((winPtr->mainPtr == dispPtr->grabWinPtr->mainPtr)
- || (dispPtr->grabFlags & GRAB_GLOBAL)) {
- appGrabbed = 1;
- }
- for (winPtr2 = winPtr; winPtr2 != dispPtr->grabWinPtr;
- winPtr2 = winPtr2->parentPtr) {
- if (winPtr2 == NULL) {
- outsideGrabTree = 1;
- break;
- }
- }
- }
-
- originalFlags = dispPtr->grabFlags;
- dispPtr->grabFlags &= ~GRAB_BUTTON_RELEASE;
- if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
- if ((eventPtr->type == EnterNotify)
- && (eventPtr->xcrossing.detail != NotifyVirtual)
- && (eventPtr->xcrossing.detail != NotifyNonlinearVirtual)) {
- if ((dispPtr->grabWinPtr == NULL)
- || (dispPtr->grabWinPtr->mainPtr == winPtr->mainPtr)) {
- dispPtr->ungrabWinPtr = winPtr;
- }
- dispPtr->serverWinPtr = winPtr;
- } else {
- dispPtr->serverWinPtr = NULL;
- }
- if (dispPtr->grabWinPtr != NULL) {
- if (eventPtr->xcrossing.mode == NotifyNormal) {
- /*
- * When a grab is active, X continues to report enter and
- * leave events for windows outside the tree of the grab
- * window. Detect these events and ignore them.
- */
-
- if (outsideGrabTree && appGrabbed) {
- return 0;
- }
-
- /*
- * Make buttons have the same grab-like behavior inside a grab
- * as they do outside a grab: do this by ignoring enter and
- * leave events except for the window in which the button was
- * pressed.
- */
-
- if ((dispPtr->buttonWinPtr != NULL)
- && (winPtr != dispPtr->buttonWinPtr)) {
- return 0;
- }
- } else if (eventPtr->xcrossing.mode == NotifyUngrab) {
- /*
- * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
- */
-
- dispPtr->grabFlags = originalFlags;
- if (outsideGrabTree && appGrabbed
- && (dispPtr->grabFlags & GRAB_BUTTON_RELEASE)) {
- /*
- * The only way we get here is if a button was pressed,
- * then moved to a different window and released. Enter
- * and leave events were deferred while the button was
- * down, but now we're getting them to move the pointer
- * back to the right window, and this particular event
- * is for a window outside the grab tree. Ignore it.
- */
-
- return 0;
- }
- }
- }
-
- /*
- * Keep track of the window containing the mouse, in order to
- * detect various bogus event sequences.
- */
-
- dispPtr->pointerWinPtr = dispPtr->serverWinPtr;
- return 1;
- }
- if ((dispPtr->grabWinPtr == NULL) || !appGrabbed) {
- return 1;
- }
-
- if (eventPtr->type == MotionNotify) {
- /*
- * When grabs are active, X reports motion events relative to the
- * window under the pointer. Instead, it should report the events
- * relative to the window the button went down in, if there is a
- * button down. Otherwise, if the pointer window is outside the
- * subtree of the grab window, the events should be reported
- * relative to the grab window. Otherwise, the event should be
- * reported to the pointer window.
- */
-
- winPtr2 = winPtr;
- if (dispPtr->buttonWinPtr != NULL) {
- winPtr2 = dispPtr->buttonWinPtr;
- } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
- winPtr2 = dispPtr->grabWinPtr;
- }
- if (winPtr2 != winPtr) {
- XEvent newEvent;
-
- newEvent = *eventPtr;
- ChangeEventWindow(&newEvent, winPtr2);
- XPutBackEvent(winPtr2->display, &newEvent);
- return 0;
- }
- return 1;
- }
-
- /*
- * Process ButtonPress and ButtonRelease events:
- * 1. Keep track of whether a button is down and what window it
- * went down in.
- * 2. If the first button goes down outside the grab tree, pretend
- * it went down in the grab window. Note: it's important to
- * redirect events to the grab window like this in order to make
- * things like menus work, where button presses outside the
- * grabbed menu need to be seen. An application can always
- * ignore the events if they occur outside its window.
- * 3. If a button press or release occurs outside the window where
- * the first button was pressed, retarget the event so it's reported
- * to the window where the first button was pressed.
- * 4. If the last button is released in a window different than where
- * the first button was pressed, generate Enter/Leave events to
- * move the mouse from the button window to its current window.
- * 5. If the grab is set at a time when a button is already down, or
- * if the window where the button was pressed was deleted, then
- * dispPtr->buttonWinPtr will stay NULL. Just forget about the
- * auto-grab for the button press; events will go to whatever
- * window contains the pointer. If this window isn't in the grab
- * tree then redirect events to the grab window.
- */
-
- if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
- winPtr2 = dispPtr->buttonWinPtr;
- if (winPtr2 == NULL) {
- if (outsideGrabTree) {
- winPtr2 = dispPtr->grabWinPtr; /* Note 5. */
- } else {
- winPtr2 = winPtr; /* Note 5. */
- }
- }
- if (eventPtr->type == ButtonPress) {
- if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
- if (outsideGrabTree) {
- XEvent newEvent;
-
- newEvent = *eventPtr;
- ChangeEventWindow(&newEvent, dispPtr->grabWinPtr);
- XPutBackEvent(dispPtr->display, &newEvent);
- return 0; /* Note 2. */
- }
- dispPtr->buttonWinPtr = winPtr;
- return 1;
- }
- } else {
- if ((eventPtr->xbutton.state & ALL_BUTTONS)
- == state[eventPtr->xbutton.button - Button1]) {
- if ((dispPtr->buttonWinPtr != winPtr)
- && (dispPtr->buttonWinPtr != NULL)) {
- XEvent newEvent; /* Note 4. */
-
- /*
- * If the button release is made with pointer outside
- * all applications, X reports it relative to the grab
- * window. Change the current window to NULL to
- * reflect that the pointer's outside everything. Do
- * the same if the pointer's in a window that's not
- * part of the grab tree.
- */
-
- if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
- winPtr = NULL;
- }
- newEvent = *eventPtr;
- newEvent.xcrossing.mode = NotifyUngrab;
- newEvent.xcrossing.focus = False;
- newEvent.xcrossing.state =
- eventPtr->xbutton.state & ~ALL_BUTTONS;
- MovePointer(&newEvent, dispPtr->buttonWinPtr, winPtr);
- }
- dispPtr->buttonWinPtr = NULL;
- dispPtr->grabFlags |= GRAB_BUTTON_RELEASE;
- }
- }
- if (winPtr2 != winPtr) {
- XEvent newEvent;
-
- newEvent = *eventPtr;
- ChangeEventWindow(&newEvent, winPtr2);
- XPutBackEvent(dispPtr->display, &newEvent);
- return 0; /* Note 3. */
- }
- }
-
- return 1;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ChangeEventWindow --
- *
- * Given an event and a new window to which the event should be
- * retargeted, modify fields of the event so that the event is
- * properly retargeted to the new window.
- *
- * Results:
- * The following fields of eventPtr are modified: window,
- * subwindow, x, y, same_screen.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ChangeEventWindow(eventPtr, winPtr)
- register XEvent *eventPtr; /* Event to retarget. Must have
- * type ButtonPress, ButtonRelease, KeyPress,
- * KeyRelease, MotionNotify, EnterNotify,
- * or LeaveNotify. */
- TkWindow *winPtr; /* New target window for event. */
- {
- int x, y, sameScreen, bd;
- register TkWindow *childPtr;
-
- eventPtr->xmotion.window = Tk_WindowId(winPtr);
- if (eventPtr->xmotion.root ==
- RootWindow(winPtr->display, winPtr->screenNum)) {
- Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
- eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
- eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
- eventPtr->xmotion.subwindow = None;
- for (childPtr = winPtr->childList; childPtr != NULL;
- childPtr = childPtr->nextPtr) {
- if (childPtr->flags & TK_TOP_LEVEL) {
- continue;
- }
- x = eventPtr->xmotion.x - childPtr->changes.x;
- y = eventPtr->xmotion.y - childPtr->changes.y;
- bd = childPtr->changes.border_width;
- if ((x >= -bd) && (y >= -bd)
- && (x < (childPtr->changes.width + bd))
- && (y < (childPtr->changes.width + bd))) {
- eventPtr->xmotion.subwindow = childPtr->window;
- }
- }
- sameScreen = 1;
- } else {
- eventPtr->xmotion.x = 0;
- eventPtr->xmotion.y = 0;
- eventPtr->xmotion.subwindow = None;
- sameScreen = 0;
- }
- if (eventPtr->type == MotionNotify) {
- eventPtr->xmotion.same_screen = sameScreen;
- } else {
- eventPtr->xbutton.same_screen = sameScreen;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * MovePointer --
- *
- * This procedure synthesizes EnterNotify and LeaveNotify events
- * to correctly transfer the pointer from one window to another.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Synthesized events may be pushed back onto the event queue.
- * The event pointed to by eventPtr is modified.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- MovePointer(eventPtr, sourcePtr, destPtr)
- XEvent *eventPtr; /* A template X event. Must have all fields
- * properly set for EnterNotify and LeaveNotify
- * events except window, subwindow, x, y,
- * detail, and same_screen. (x_root and y_root
- * must be valid, even though x and y needn't
- * be valid). */
- TkWindow *sourcePtr; /* Window currently containing pointer (NULL
- * means it's not one managed by this
- * process). */
- TkWindow *destPtr; /* Window that is to end up containing the
- * pointer (NULL means it's not one managed
- * by this process). */
- {
- TkDisplay *dispPtr;
- register TkWindow *ancestorPtr; /* Lowest ancestor shared between
- * sourcePtr and destPtr, or
- * sourcePtr's top-level window if no
- * shared ancestor. */
- register TkWindow *winPtr;
- int upLevels, downLevels, i, j;
-
- /*
- * There are four possible cases to deal with:
- *
- * 1. SourcePtr and destPtr are the same. There's nothing to do in
- * this case.
- * 2. SourcePtr is an ancestor of destPtr in the same top-level
- * window. Must generate events down the window tree from source
- * to dest.
- * 3. DestPtr is an ancestor of sourcePtr in the same top-level
- * window. Must generate events up the window tree from sourcePtr
- * to destPtr.
- * 4. All other cases. Must first generate events up the window tree
- * from sourcePtr to its top-level, then down from destPtr's
- * top-level to destPtr. This form is called "non-linear."
- *
- * The code below separates these four cases and decides how many levels
- * up and down events have to be generated for.
- */
-
- if (sourcePtr == destPtr) {
- return;
- }
-
- /*
- * Mark destPtr and all of its ancestors with a special flag bit.
- */
-
- if (destPtr != NULL) {
- dispPtr = destPtr->dispPtr;
- for (winPtr = destPtr; ; winPtr = winPtr->parentPtr) {
- winPtr->flags |= TK_GRAB_FLAG;
- if (winPtr->flags & TK_TOP_LEVEL) {
- break;
- }
- }
- } else {
- dispPtr = sourcePtr->dispPtr;
- }
-
- /*
- * Search upwards from sourcePtr until an ancestor of destPtr is
- * found or a top-level window is reached. Remember if we pass out
- * of the grab tree along the way, since this means we'll have to
- * skip some of the events that would otherwise be generated.
- */
-
- ancestorPtr = sourcePtr;
- upLevels = 0;
- if (sourcePtr != NULL) {
- for (; ; upLevels++, ancestorPtr = ancestorPtr->parentPtr) {
- if (ancestorPtr->flags & TK_GRAB_FLAG) {
- break;
- }
- if (ancestorPtr->flags & TK_TOP_LEVEL) {
- upLevels++;
- break;
- }
- }
- }
-
- /*
- * Search upwards from destPtr again, clearing the flag bits and
- * remembering how many levels up we had to go.
- */
-
- if (destPtr == NULL) {
- downLevels = 0;
- } else {
- downLevels = -1;
- for (i = 0, winPtr = destPtr; ; i++, winPtr = winPtr->parentPtr) {
- winPtr->flags &= ~TK_GRAB_FLAG;
- if (winPtr == ancestorPtr) {
- downLevels = i;
- }
- if (winPtr->flags & TK_TOP_LEVEL) {
- if (downLevels == -1) {
- downLevels = i+1;
- }
- break;
- }
- }
- }
-
- /*
- * Generate enter/leave events and push them back onto the event
- * queue. This has to be done backwards, since the last event
- * pushed will be the first one processed.
- */
-
- #define PUSH_EVENT(w, t, d) \
- if (w->window != None) { \
- eventPtr->type = t; \
- eventPtr->xcrossing.detail = d; \
- ChangeEventWindow(eventPtr, w); \
- XPutBackEvent(w->display, eventPtr); \
- }
-
- if (downLevels == 0) {
-
- /*
- * SourcePtr is an inferior of destPtr.
- */
-
- if (destPtr != NULL) {
- PUSH_EVENT(destPtr, EnterNotify, NotifyInferior);
- }
- for (i = upLevels-1; i > 0; i--) {
- for (winPtr = sourcePtr, j = 0; j < i;
- winPtr = winPtr->parentPtr, j++) {
- if (winPtr == dispPtr->grabWinPtr) {
- goto nextIteration;
- }
- }
- PUSH_EVENT(winPtr, LeaveNotify, NotifyVirtual);
- nextIteration: continue;
- }
- PUSH_EVENT(sourcePtr, LeaveNotify, NotifyAncestor);
- } else if (upLevels == 0) {
-
- /*
- * DestPtr is an inferior of sourcePtr.
- */
-
- if (destPtr != NULL) {
- PUSH_EVENT(destPtr, EnterNotify, NotifyAncestor);
- }
- for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
- winPtr = winPtr->parentPtr, i--) {
- PUSH_EVENT(winPtr, EnterNotify, NotifyVirtual);
- }
- if (sourcePtr != NULL) {
- PUSH_EVENT(sourcePtr, LeaveNotify, NotifyInferior);
- }
- } else {
-
- /*
- * Non-linear: neither window is an inferior of the other.
- */
-
- if (destPtr != NULL) {
- PUSH_EVENT(destPtr, EnterNotify, NotifyNonlinear);
- }
- if (destPtr != dispPtr->grabWinPtr) {
- for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
- winPtr = winPtr->parentPtr, i--) {
- PUSH_EVENT(winPtr, EnterNotify, NotifyNonlinearVirtual);
- if (winPtr == dispPtr->grabWinPtr) {
- break;
- }
- }
- }
- for (i = upLevels-1; i > 0; i--) {
- for (winPtr = sourcePtr, j = 0; j < i;
- winPtr = winPtr->parentPtr, j++) {
- if (winPtr == dispPtr->grabWinPtr) {
- goto nextWindow;
- }
- }
- PUSH_EVENT(winPtr, LeaveNotify, NotifyNonlinearVirtual);
- nextWindow: continue;
- }
- PUSH_EVENT(sourcePtr, LeaveNotify, NotifyNonlinear);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * MovePointer2 --
- *
- * This procedure synthesizes EnterNotify and LeaveNotify events
- * to correctly transfer the pointer from one window to another.
- * It is different from MovePointer in that no template X event
- * needs to be supplied; this procedure generates the template
- * event and calls MovePointer.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Synthesized events may be pushed back onto the event queue.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- MovePointer2(sourcePtr, destPtr, mode)
- TkWindow *sourcePtr; /* Window currently containing pointer (NULL
- * means it's not one managed by this
- * process). */
- TkWindow *destPtr; /* Window that is to end up containing the
- * pointer (NULL means it's not one managed
- * by this process). */
- int mode; /* Mode for enter/leave events, such as
- * NotifyNormal or NotifyUngrab. */
- {
- XEvent event;
- Window dummy1, dummy2;
- int dummy3, dummy4;
- TkWindow *winPtr;
-
- winPtr = sourcePtr;
- if ((winPtr == NULL) || (winPtr->window == None)) {
- winPtr = destPtr;
- if ((winPtr == NULL) || (winPtr->window == None)) {
- return;
- }
- }
-
- event.xcrossing.serial = LastKnownRequestProcessed(winPtr->display);
- event.xcrossing.send_event = False;
- event.xcrossing.display = winPtr->display;
- event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum);
- event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
- XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
- &event.xcrossing.x_root, &event.xcrossing.y_root,
- &dummy3, &dummy4, &event.xcrossing.state);
- event.xcrossing.mode = mode;
- event.xcrossing.focus = False;
- MovePointer(&event, sourcePtr, destPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkGrabDeadWindow --
- *
- * This procedure is invoked whenever a window is deleted, so that
- * grab-related cleanup can be performed.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Various cleanups happen, such as generating events to move the
- * pointer back to its "natural" window as if an ungrab had been
- * done. See the code.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkGrabDeadWindow(winPtr)
- register TkWindow *winPtr; /* Window that is in the process
- * of being deleted. */
- {
- TkDisplay *dispPtr = winPtr->dispPtr;
-
- if (dispPtr->grabWinPtr == winPtr) {
- dispPtr->grabWinPtr = NULL;
- if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {
- /*
- * Must generate enter/leave events to move back to the window
- * that contains the mouse pointer. We needn't filter events
- * here like we do in Tk_Ungrab because there are no children
- * of the grab window left in existence.
- */
-
- movePointerBack:
- if ((dispPtr->ungrabWinPtr != NULL)
- && (dispPtr->ungrabWinPtr->mainPtr != winPtr->mainPtr)) {
- dispPtr->ungrabWinPtr = NULL;
- }
- MovePointer2(winPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
- }
- } else if (dispPtr->buttonWinPtr == winPtr) {
- /*
- * The window in which a button was pressed was deleted. Simulate
- * dropping the button auto-grab by generating Enter and Leave
- * events to move the pointer back to the window it's really on
- * top of.
- */
-
- dispPtr->buttonWinPtr = NULL;
- goto movePointerBack;
- }
- if (dispPtr->ungrabWinPtr == winPtr) {
- dispPtr->ungrabWinPtr = NULL;
- }
- if (dispPtr->pointerWinPtr == winPtr) {
- dispPtr->pointerWinPtr = NULL;
- }
- if (dispPtr->serverWinPtr == winPtr) {
- dispPtr->serverWinPtr = NULL;
- }
- }
-